#include "./cmd_uart.h"
#include "../device/device.h"
#include "../utils/codec.h"

namespace driver {

utils::Object<CmdUart_t> cmdUart;
// PB0 <=> RXD0
// PB1 <=> TXD0
CmdUart_t::CmdUart_t() {

    GPIO_InitStruType gpio;
    UART_InitStruType uart;

    gpio.GPIO_Signal = GPIO_Pin_Signal_Digital;
    gpio.GPIO_Func = GPIO_Func_2;
    gpio.GPIO_Direction = GPIO_Dir_Out;
    gpio.GPIO_PUEN = GPIO_PUE_Input_Disable;
    gpio.GPIO_PDEN = GPIO_PDE_Input_Disable;
    gpio.GPIO_OD = GPIO_ODE_Output_Disable;
    gpio.GPIO_DS = GPIO_DS_Output_Strong;
    GPIO_Init(GPIOB, GPIO_Pin_1, &gpio);

    gpio.GPIO_Direction = GPIO_Dir_In;
    gpio.GPIO_PUEN = GPIO_PUE_Input_Enable;
    GPIO_Init(GPIOB, GPIO_Pin_0, &gpio);

    uart.UART_StopBits = UART_StopBits_1;
    uart.UART_TxMode = UART_DataMode_8;
    uart.UART_TxPolar = UART_Polar_Normal;
    uart.UART_RxMode = UART_DataMode_8;
    uart.UART_RxPolar = UART_Polar_Normal;
    uart.UART_BaudRate = 9600;
    uart.UART_ClockSet = UART_Clock_1;
    UART_Init(UART0, &uart);
	
	write_unlock();

    UART_TBIMConfig(UART0, UART_TRBIM_Byte);
    UART_RBIMConfig(UART0, UART_TRBIM_Byte);
    UART_ITConfig(UART0, UART_IT_RB, ENABLE);
    UART_ITConfig(UART0, UART_IT_TXIDLE, ENABLE);
    NVIC_EnableIRQ(UART0_IRQn);
    UART0_TxEnable();
    UART0_RxEnable();
}

bool CmdUart_t::next_uint16(uint16_t &val) {
    uint8_t h, l;
    if (!next_byte(h)) {
        return false;
    }
    if (!next_byte(l)) {
        return false;
    }
    val = (h << 8) + l;
    return true;
}

utils::Error_t CmdUart_t::recv_proto(utils::ByteView_t& bw) {
    uint8_t val;
    if (!next_byte(val, 100)) {
        return utils::ec::RecvTimeout;
    }
    if (val != utils::dtu_proto::ReadHead) {
        return utils::ec::HeadFail;
    }
    
    if (!next_byte(val)) {
        return utils::ec::RecvTimeout;
    }
    if (val < 5) {
        return utils::ec::LenFail;
    }

    uint8_t len = val - 4;
    for (uint8_t i = 0; i < len; i ++) {
        if (!next_byte(val)) {
            return utils::ec::RecvTimeout;
        }
        bw.buf[i] = val;
    }

    uint16_t end;
    if (!next_uint16(end)) {
        return utils::ec::RecvTimeout;
    }

    if (end != utils::dtu_proto::End) {
        return utils::ec::EndFail;
    }
    bw.len = len;
    return utils::ec::Ok;
}

void CmdUart_t::write_ack(uint8_t ack, uint32_t id) {
    using namespace utils;
    uint8_t buf[9];

    encode_uint8(buf, 0x1C);
    encode_uint8(buf + 1, 0x09);
    encode_uint8(buf + 2, ack);
    encode_uint32(buf + 3, id);
    encode_uint16(buf + 7, dtu_proto::End);

    write(buf, 9);
}

void CmdUart_t::write(const uint8_t *buf, uint32_t size) {
    write_lock();
    write_impl(buf, size);
    write_unlock();
}

void CmdUart_t::recv_byte(uint8_t c) {
    m_mq.put(c);
}

void CmdUart_t::write_impl(const uint8_t *buf, uint32_t size) {
    tx_clear();
    for (uint32_t i = 0; i < size; i ++) {
        uint8_t c = buf[i];
        UART_SendByte(UART0, c);
        tx_wait();
    }
}

}

using namespace driver;

extern "C"
void UART0_IRQHandler() {

    if (UART_GetFlagStatus(UART0, UART_FLAG_RB) != RESET) {
        uint8_t c = UART_RecByte(UART0);
        cmdUart->recv_byte(c);
        return;
    }

    if (UART_GetFlagStatus(UART0, UART_FLAG_TXIDLE) != RESET) {
        UART_ClearITPendingBit(UART0, UART_Clr_TXIDLE);
        cmdUart->tx_signal();
    }
}

